# version: 1.02
# date: 2024/Dec/12
# provided by: 艾雪 & Copilot

import tkinter as tk
from tkinter import simpledialog, scrolledtext
# import pandas as pd
# import numpy as np
# import os as os 
import json

# 读取danyao.json文件
with open('danyao.json', 'r', encoding='utf-8') as f:
    danyao_data = json.load(f)

# 读取yaocai.json文件
with open('yaocai.json', 'r', encoding='utf-8') as f:
    yaocai_data = json.load(f)

# 输出所有“all_num”=999999的danyao的“name”
# for key, value in danyao_data.items():
#     if value.get('all_num') == 999999:
#         print(value.get('name'))

def peifang(yaocai_data, type0, num0, type1, num1, sum=0): 
    result = []
    zhu = []
    yin = []
    fu = []
    
    # 遍历所有药材，寻找满足条件的主药、药引和辅药
    for zhu_key, zhu_value in yaocai_data.items():
        if (zhu_value['主药']['type'] == type0 and 
            zhu_value['主药']['power'] == num0):
            for yin_key, yin_value in yaocai_data.items():
                if (yin_value['药引']['type'] == type0 and 
                    yin_value['药引']['power'] == num0 and 
                    zhu_value['主药']['h_a_c']['type'] * zhu_value['主药']['h_a_c']['power'] + 
                    yin_value['药引']['h_a_c']['type'] * yin_value['药引']['h_a_c']['power'] == sum):
                    for fu_key, fu_value in yaocai_data.items():
                        if (fu_value['辅药']['type'] == type1 and 
                            fu_value['辅药']['power'] == num1):
                            zhu.append(zhu_value['name'])
                            yin.append(yin_value['name'])
                            fu.append(fu_value['name'])
                            result.append([zhu_value['name'], yin_value['name'], fu_value['name']])
    # yaocai_useful = list(set(zhu+yin+fu))
    # msgs = f'主药:{set(zhu)}\n药引:{set(yin)}\n辅药:{set(fu)}'
    # print(msgs)
    # print(f"需要主药:{set(zhu)}\n")
    # print(f"需要药引:{set(yin)}\n")
    # print(f"需要辅药:{set(fu)}\n")
    return result
    # return msgs


def printpeifang(yaocais: list):
    msg = f"配方：主药{yaocais[0]}1药引{yaocais[1]}1辅药{yaocais[2]}1丹炉寒铁铸心炉\n"
    return msg

def listadd(a: list, b: list):
    return list(set(a)).union(set(b))

def xpinyaocai(x: int, yaocai_data):
    results= peifang(yaocai_data, 2, 2**x, 6, 2**x, 0) + peifang(yaocai_data, 2, 2**x, 3, 2**x, 0)
    # result0, yaocai0 = peifang(yaocai_data, 2, 2**x, 6, 2**x, 0)
    # result1, yaocai1 = peifang(yaocai_data, 2, 2**x, 3, 2**x, 0) 
    # results = listadd(result0,result1)
    # yaocais = listadd(yaocai0,yaocai1)
    # with open(f'all/result{x}.txt', 'w', encoding='utf-8') as f:
    #     msg0 = f"所有{x}品药材组成的丹方有：\n"
    #     f.write(msg0)
    #     # print(msg0)
    #     for term in results:
    #         msg1 = printpeifang(term)
    #         f.write(msg1)
    #         # print(msg1)
    #     print("fin.")
    return results
    # print(results)


def parse_stock(input_str, yaocai_data):
    stock = {}
    lines = input_str.split('\n')
    for i in range(0, len(lines), 2):
        name_line = lines[i]
        count_line = lines[i + 1]
        
        name = name_line.split('：')[1].strip()
        count = int(count_line.split(':')[1].split('---')[0].strip())
        
        # 检查yaocai_data中是否存在该name
        if any(yaocai['name'] == name for yaocai in yaocai_data.values()):
            stock[name] = count
    
    return stock


from collections import defaultdict

def best_match(results, stock):
    # Convert stock to a tuple for state representation
    stock_items = list(stock.keys())
    initial_state = tuple(stock[item] for item in stock_items)
    
    # Initialize DP dictionary
    dp = defaultdict(lambda: (0, []))
    dp[initial_state] = (0, [])
    
    for term in results:
        new_dp = dp.copy()
        for state, (count, terms) in dp.items():
            temp_state = list(state)
            valid_term = True
            
            for name in term:
                if name in stock_items:
                    index = stock_items.index(name)
                    if temp_state[index] > 0:
                        temp_state[index] -= 1
                    else:
                        valid_term = False
                        break
                else:
                    valid_term = False
                    break
            
            if valid_term:
                new_state = tuple(temp_state)
                new_count = count + 1
                new_terms = terms + [term]
                if new_count > new_dp[new_state][0]:
                    new_dp[new_state] = (new_count, new_terms)
        
        dp = new_dp
    
    max_count, bestmatch = max(dp.values(), key=lambda x: x[0])
    return max_count, bestmatch

from collections import Counter
from itertools import combinations

def dedup_list_of_lists(results):
    """
    输入一个二维列表(list of list of str),返回一个去重的一维列表(list of str)。
    """
    deduped_elements = set()
    for inner_list in results:
        for element in inner_list:
            deduped_elements.add(element)
    return list(deduped_elements)

def min_remaining_stock(results, stock):
    # 将stock转换为Counter对象
    stock_counter = Counter(stock)
    options = dedup_list_of_lists(results)
    # 初始化购买记录和配方记录
    options_bought = Counter()
    formulas_used = []
    
    # 构建反向索引
    reverse_index = {}
    for term in results:
        for pair in combinations(term, 2):
            reverse_index.setdefault(pair, []).append(term)
    
    # 检查stock中的每个pair是否可以通过购买一个药材组成triplet
    for pair in combinations(stock_counter.keys(), 2):
        if stock_counter[pair[0]] >= 1 and stock_counter[pair[1]] >= 1:
            if pair in reverse_index:
                for term in reverse_index[pair]:
                    missing_name = (set(term) - set(pair)).pop()
                    if missing_name in options:
                        pair_count = min(stock_counter[pair[0]], stock_counter[pair[1]])
                        options_bought[missing_name] += pair_count
                        formulas_used.extend([term] * pair_count)
                        stock_counter.subtract(Counter(pair * pair_count))
    
    for name, count in stock_counter.items():
        if count >= 2:
            pair = (name, name)
            if pair in reverse_index:
                for term in reverse_index[pair]:
                    missing_name = (set(term) - set(pair)).pop()
                    if missing_name in options:
                        pair_count = count // 2
                        options_bought[missing_name] += pair_count
                        formulas_used.extend([term] * pair_count)
                        stock_counter[name] %= 2
    
    return dict(options_bought), formulas_used

def liandan(level,input_str):
    results=xpinyaocai(level, yaocai_data)
    stock = parse_stock(input_str, yaocai_data)
    # print("库存:", stock)
    result = f"库存:{stock}\n"
    max_count, bestmatch = best_match(results, stock)
    # print("可炼丹次数:", max_count)
    result += f"可炼丹次数:{max_count}\n"
    for term in bestmatch:
        msg=printpeifang(term) 
        result += msg

    remaining_stock = stock.copy()
    for term in bestmatch:
        for name in term:
            if name in remaining_stock and remaining_stock[name] > 0:
                remaining_stock[name] -= 1
    result += f'剩余库存：{remaining_stock}\n'
    options_bought, formulas_used = min_remaining_stock(results, remaining_stock)
    result += f"推荐购买药材：{options_bought}\n"
    for term in formulas_used:
        msg=printpeifang(term) 
        result += msg
    return result


def main():
    # 创建主窗口
    root = tk.Tk()
    root.title("汐汐炼丹助手_v1.01")

    # 创建并布局药材品阶输入框（level）
    level_label = tk.Label(root, text="药材品阶")
    level_label.grid(row=0, column=0, padx=10, pady=10, sticky="w")
    level_entry = tk.Entry(root)
    level_entry.grid(row=0, column=1, padx=10, pady=10, sticky="e")
    level_entry.insert(0, "输入数字，如：4")

    # 创建并布局药材背包输入框（stock_str）
    stock_label = tk.Label(root, text="药材背包")
    stock_label.grid(row=0, column=2, padx=10, pady=10, sticky="w")
    stock_str = scrolledtext.ScrolledText(root, width=40, height=10)
    stock_str.grid(row=0, column=3, padx=10, pady=10)
    stock_str.insert(tk.END, """输入药材背包中该品阶药材内容，如：
名字：乌稠木
拥有数量:2---炼金
名字：鸡冠草
拥有数量:2---炼金""")

    # 创建并布局按钮BOOM
    def submit():
        try:
            level = int(level_entry.get())
            if level <= 0:
                raise ValueError("品阶须为正整数。")
        except ValueError as e:
            result_text.config(state=tk.NORMAL)
            result_text.delete("1.0", tk.END)
            result_text.insert(tk.END, f"品阶非法输入: {e}")
            result_text.config(state=tk.DISABLED)
            return
        
        stock_value = stock_str.get("1.0", tk.END).strip()
        if not stock_value:
            result_text.config(state=tk.NORMAL)
            result_text.delete("1.0", tk.END)
            result_text.insert(tk.END, "库存非法输入！")
            result_text.config(state=tk.DISABLED)
            return

        result = liandan(level, stock_value)
        result_text.config(state=tk.NORMAL)
        result_text.delete("1.0", tk.END)
        result_text.insert(tk.END, result)
        result_text.config(state=tk.DISABLED)

    submit_button = tk.Button(root, text="BOOM!", command=submit)
    submit_button.grid(row=1, column=0, padx=10, pady=10)

    # 创建并布局输出框，显示liandan()的返回值
    result_text = scrolledtext.ScrolledText(root, width=40, height=10)
    result_text.grid(row=1, column=3, padx=10, pady=10)
    result_text.config(state=tk.DISABLED)

    # 使输入框和输出框的尺寸可以随着对话窗口尺寸调整而调整
    root.grid_columnconfigure(1, weight=1)
    root.grid_columnconfigure(3, weight=1)
    root.grid_rowconfigure(0, weight=1)
    root.grid_rowconfigure(1, weight=1)

    # 运行应用程序
    root.mainloop()

if __name__ == "__main__":
    main()